﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace Nirvana
{
    public abstract class RenderEffectManager
    {
        private LinkedList<RenderEffect> staticRenderEffectList = new LinkedList<RenderEffect>();
        private LinkedList<RenderEffect> dynamicRenderEffectList = new LinkedList<RenderEffect>();
        private Dictionary<NirvanaRenderer, RenderEffect[]> nirvanaRendererMap = new Dictionary<NirvanaRenderer, RenderEffect[]>();
        private List<RenderEffect> allRenderEffectList = new List<RenderEffect>();
        private LinkedListNode<RenderEffectManager> renderEffectMgr;

        public RenderEffectManager()
        {
            renderEffectMgr = NirvanaRenderer.AddRendererEffectMgr(this);
        }
        ~RenderEffectManager()
        {
            NirvanaRenderer.RemoveRendererEffectMgr(this.renderEffectMgr);
        }

        public abstract void SetupRendererStart(NirvanaRenderer renderer, ref ShaderKeywords keywords, ref MaterialPropertyBlock propertyBlock);
        public abstract void SetupRendererEffect(NirvanaRenderer renderer, RenderEffect effect, ref ShaderKeywords keywords, ref MaterialPropertyBlock propertyBlock);
        public abstract void SetupRendererEnd(NirvanaRenderer renderer, ref ShaderKeywords keywords, ref MaterialPropertyBlock propertyBlock);


        public void FindEffects(NirvanaRenderer renderer, List<RenderEffect> effects)
        {
            foreach (var p in this.staticRenderEffectList)
            {
                if (p.IsTakeEffect(renderer))
                {
                    effects.Add(p);
                }
            }
            foreach (var p in this.dynamicRenderEffectList)
            {
                if (p.IsTakeEffect(renderer))
                {
                    effects.Add(p);
                }
            }
        }

        public LinkedListNode<RenderEffect> AddStatic(RenderEffect renderEffect)
        {
            return this.staticRenderEffectList.AddLast(renderEffect);
        }

        public void RemoveStatic(LinkedListNode<RenderEffect> node)
        {
            this.staticRenderEffectList.Remove(node);
        }


        public LinkedListNode<RenderEffect> AddDynamic(RenderEffect renderEffect)
        {
            return this.dynamicRenderEffectList.AddLast(renderEffect);
        }

        public void RemoveDynamic(LinkedListNode<RenderEffect> node)
        {
            this.dynamicRenderEffectList.Remove(node);
        }


        public void SetupRenderer(NirvanaRenderer render, ref ShaderKeywords keys, ref MaterialPropertyBlock block)
        {
            this.SetupRendererStart(render, ref keys, ref block);
            if (render.IsStatic)
            {
                RenderEffect[] renderEffects;
                if(!this.nirvanaRendererMap.TryGetValue(render,out renderEffects))
                {
                    this.allRenderEffectList.Clear();
                    foreach(var p in staticRenderEffectList)
                    {
                        if (p.IsTakeEffect(render))
                        {
                            this.allRenderEffectList.Add(p);
                            this.SetupRendererEffect(render, p, ref keys, ref block);
                        }
                    }
                    this.nirvanaRendererMap.Add(render, this.allRenderEffectList.ToArray());
                }
                else
                {
                    foreach (var p in renderEffects)
                    {
                        if (p.IsTakeEffect(render))
                        {
                            this.SetupRendererEffect(render, p, ref keys, ref block);
                        }
                    }
                }
            }
            else
            {
                foreach (var p in staticRenderEffectList)
                {
                    if (p.IsTakeEffect(render))
                    {
                        this.SetupRendererEffect(render, p, ref keys, ref block);
                    }
                }
            }

            foreach(var p in dynamicRenderEffectList)
            {
                if (p.IsTakeEffect(render))
                {
                    this.SetupRendererEffect(render, p, ref keys, ref block);
                }
            }
            this.SetupRendererEnd(render, ref keys, ref block);

        }



    }

}

